<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>Spring源码系列-第11章-SpringMVC异常处理源码和@EnableWebMvc原理 | 风祈的时光录</title><meta name="keywords" content="Spring，框架，spring源码"><meta name="author" content="youthlql"><meta name="copyright" content="youthlql"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="SpringMVC异常处理源码和@EnableWebMvc原理">
<meta property="og:type" content="article">
<meta property="og:title" content="Spring源码系列-第11章-SpringMVC异常处理源码和@EnableWebMvc原理">
<meta property="og:url" content="https://imlql.cn/post/4dfb096b.html">
<meta property="og:site_name" content="风祈的时光录">
<meta property="og:description" content="SpringMVC异常处理源码和@EnableWebMvc原理">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://npm.elemecdn.com/lql_static@latest/logo/spring.png">
<meta property="article:published_time" content="2023-01-05T04:01:02.000Z">
<meta property="article:modified_time" content="2023-01-05T15:24:36.509Z">
<meta property="article:author" content="youthlql">
<meta property="article:tag" content="Spring源码">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://npm.elemecdn.com/lql_static@latest/logo/spring.png"><link rel="shortcut icon" href="https://npm.elemecdn.com/youthlql@1.0.8/blog/favicon.png"><link rel="canonical" href="https://imlql.cn/post/4dfb096b"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//hm.baidu.com"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://npm.elemecdn.com/@fortawesome/fontawesome-free@6/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.css" media="print" onload="this.media='all'"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?f693ff99cc7e613b88cf5b729a14b48b";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'Spring源码系列-第11章-SpringMVC异常处理源码和@EnableWebMvc原理',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-01-05 23:24:36'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/ali_icon.css"><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/2-24-mogai.css"><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/atom.xml" title="风祈的时光录" type="application/atom+xml">
</head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/avatar/2.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data is-center"><div class="data-item"><a href="/archives/"><div class="headline">文章</div><div class="length-num">37</div></a></div><div class="data-item"><a href="/tags/"><div class="headline">标签</div><div class="length-num">21</div></a></div><div class="data-item"><a href="/categories/"><div class="headline">分类</div><div class="length-num">13</div></a></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://npm.elemecdn.com/lql_static@latest/logo/spring.png')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">风祈的时光录</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">Spring源码系列-第11章-SpringMVC异常处理源码和@EnableWebMvc原理</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2023-01-05T04:01:02.000Z" title="发表于 2023-01-05 12:01:02">2023-01-05</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-01-05T15:24:36.509Z" title="更新于 2023-01-05 23:24:36">2023-01-05</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Spring/">Spring</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Spring/%E6%BA%90%E7%A0%81V1/">源码V1</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">5.2k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>25分钟</span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="第11章-SpringMVC异常处理源码和-EnableWebMvc原理"><a href="#第11章-SpringMVC异常处理源码和-EnableWebMvc原理" class="headerlink" title="第11章-SpringMVC异常处理源码和@EnableWebMvc原理"></a>第11章-SpringMVC异常处理源码和@EnableWebMvc原理</h1><h2 id="视图解析器不重要了，不细述了"><a href="#视图解析器不重要了，不细述了" class="headerlink" title="视图解析器不重要了，不细述了"></a>视图解析器不重要了，不细述了</h2><p>因为现在都是前后端分离的架构，不太需要视图解析器了，有兴趣的可以自己研究。</p>
<h2 id="异常处理流程"><a href="#异常处理流程" class="headerlink" title="异常处理流程"></a>异常处理流程</h2><h3 id="测试类"><a href="#测试类" class="headerlink" title="测试类"></a>测试类</h3><h4 id="HelloController"><a href="#HelloController" class="headerlink" title="HelloController"></a>HelloController</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.imlql.web.controller;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="title">HelloController</span><span class="params">()</span></span>&#123;</span><br><span class="line">      System.out.println(<span class="string">&quot;HelloController.....&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> String <span class="title">sayHello</span><span class="params">(<span class="meta">@RequestParam(name = &quot;name&quot;,required = true)</span> String name, Integer i)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">int</span> x = <span class="number">10</span> / i;</span><br><span class="line">		<span class="keyword">return</span> <span class="string">&quot;index.jsp&quot;</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="什么参数都不传"><a href="#什么参数都不传" class="headerlink" title="什么参数都不传"></a>什么参数都不传</h3><p>什么参数都不传，肯定是会报异常的，因为@RequestParam那里我加了个required。</p>
<h4 id="DispatcherServlet-doDispatch"><a href="#DispatcherServlet-doDispatch" class="headerlink" title="DispatcherServlet#doDispatch()"></a>DispatcherServlet#doDispatch()</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021143709604.png"/>



<p>报了一个缺少参数的错误，下面看看怎么处理的</p>
<h4 id="DispatcherServlet-processDispatchResult"><a href="#DispatcherServlet-processDispatchResult" class="headerlink" title="DispatcherServlet#processDispatchResult()"></a>DispatcherServlet#processDispatchResult()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">processDispatchResult</span><span class="params">(HttpServletRequest request, HttpServletResponse response,</span></span></span><br><span class="line"><span class="function"><span class="params">      <span class="meta">@Nullable</span> HandlerExecutionChain mappedHandler, <span class="meta">@Nullable</span> ModelAndView mv,</span></span></span><br><span class="line"><span class="function"><span class="params">      <span class="meta">@Nullable</span> Exception exception)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">boolean</span> errorView = <span class="keyword">false</span>;</span><br><span class="line">   <span class="comment">//如果有异常处理异常，以下if内全是异常处理环节</span></span><br><span class="line">   <span class="keyword">if</span> (exception != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (exception <span class="keyword">instanceof</span> ModelAndViewDefiningException) &#123;</span><br><span class="line">         logger.debug(<span class="string">&quot;ModelAndViewDefiningException encountered&quot;</span>, exception);</span><br><span class="line">         mv = ((ModelAndViewDefiningException) exception).getModelAndView();</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> &#123;  <span class="comment">//定义无数种异常解析器就会得到不同的异常解析效果</span></span><br><span class="line">         Object handler = (mappedHandler != <span class="keyword">null</span> ? mappedHandler.getHandler() : <span class="keyword">null</span>);</span><br><span class="line">         mv = processHandlerException(request, response, handler, exception); <span class="comment">//处理异常，所有的异常解析器都不能干活，这个异常就抛出去了</span></span><br><span class="line">         errorView = (mv != <span class="keyword">null</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//上面所有的异常解析器都没能处理这个异常，下面直接炸....</span></span><br><span class="line">   <span class="comment">// 动态策略。 Did the handler return a view to render?   为啥？@ResponseBody（提前在解析返回值的时候，就已经把数据写出去了，所以这一步就没有了）</span></span><br><span class="line">   <span class="keyword">if</span> (mv != <span class="keyword">null</span> &amp;&amp; !mv.wasCleared()) &#123;</span><br><span class="line">      render(mv, request, response); <span class="comment">//渲染ModeAndView，来解析模型和视图；最终决定响应效果</span></span><br><span class="line">      <span class="keyword">if</span> (errorView) &#123;</span><br><span class="line">         WebUtils.clearErrorRequestAttributes(request);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (logger.isTraceEnabled()) &#123;</span><br><span class="line">         logger.trace(<span class="string">&quot;No view rendering, null ModelAndView returned.&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) &#123;</span><br><span class="line">      <span class="comment">// Concurrent handling started during a forward</span></span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (mappedHandler != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="comment">// Exception (if any) is already handled..</span></span><br><span class="line">      mappedHandler.triggerAfterCompletion(request, response, <span class="keyword">null</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021144337185.png"/>



<h4 id="DispatcherServlet-processHandlerException-准备处理异常"><a href="#DispatcherServlet-processHandlerException-准备处理异常" class="headerlink" title="DispatcherServlet#processHandlerException()准备处理异常"></a>DispatcherServlet#processHandlerException()准备处理异常</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">processHandlerException</span><span class="params">(HttpServletRequest request, HttpServletResponse response,</span></span></span><br><span class="line"><span class="function"><span class="params">      <span class="meta">@Nullable</span> Object handler, Exception ex)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Success and error responses may use different content types</span></span><br><span class="line">   request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Check registered HandlerExceptionResolvers...</span></span><br><span class="line">   ModelAndView exMv = <span class="keyword">null</span>; <span class="comment">//所有异常解析器继续解析</span></span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.handlerExceptionResolvers != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">for</span> (HandlerExceptionResolver resolver : <span class="keyword">this</span>.handlerExceptionResolvers) &#123;</span><br><span class="line">         exMv = resolver.resolveException(request, response, handler, ex);</span><br><span class="line">         <span class="keyword">if</span> (exMv != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (exMv != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (exMv.isEmpty()) &#123;</span><br><span class="line">         request.setAttribute(EXCEPTION_ATTRIBUTE, ex);</span><br><span class="line">         <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// We might still need view name translation for a plain error model...</span></span><br><span class="line">      <span class="keyword">if</span> (!exMv.hasView()) &#123;</span><br><span class="line">         String defaultViewName = getDefaultViewName(request);</span><br><span class="line">         <span class="keyword">if</span> (defaultViewName != <span class="keyword">null</span>) &#123;</span><br><span class="line">            exMv.setViewName(defaultViewName);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (logger.isTraceEnabled()) &#123;</span><br><span class="line">         logger.trace(<span class="string">&quot;Using resolved error view: &quot;</span> + exMv, ex);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">         logger.debug(<span class="string">&quot;Using resolved error view: &quot;</span> + exMv);</span><br><span class="line">      &#125;</span><br><span class="line">      WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());</span><br><span class="line">      <span class="keyword">return</span> exMv;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">throw</span> ex; <span class="comment">//如果所有的异常解析器都不能解析就直接抛出这个异常</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021150026618.png"/>

<h5 id="三个异常解析器概述"><a href="#三个异常解析器概述" class="headerlink" title="三个异常解析器概述"></a>三个异常解析器概述</h5><ul>
<li><p>ExceptionHandlerExceptionResolver：所有@ExceptionHandler注解方式的异常处理由他来做，启动扫描了容器中所有标了@ControllerAdvice的类以及这个类里面所有@Exceptionhandler标注的方法，并且缓存这个方法能处理什么异常</p>
</li>
<li><p>ResponseStatusExceptionResolver：找异常类上有没有@ResponseStatus注解</p>
</li>
<li><p>DefaultHandlerExceptionResolver：异常是否是spring内部指定的异常，如果是，直接响应错误页sendError以及错误代码， 并返回new的空的ModelAndView（注意这里返回的是空，不是null）</p>
</li>
</ul>
<h4 id="AbstractHandlerExceptionResolver-resolveException-解析异常"><a href="#AbstractHandlerExceptionResolver-resolveException-解析异常" class="headerlink" title="AbstractHandlerExceptionResolver#resolveException()解析异常"></a>AbstractHandlerExceptionResolver#resolveException()解析异常</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">   <span class="meta">@Override</span></span><br><span class="line"><span class="meta">@Nullable</span>  <span class="comment">//父类抽象类规定的模板</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ModelAndView <span class="title">resolveException</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">         HttpServletRequest request, HttpServletResponse response, <span class="meta">@Nullable</span> Object handler, Exception ex)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (shouldApplyTo(request, handler)) &#123;</span><br><span class="line">         prepareResponse(ex, response);</span><br><span class="line">         ModelAndView result = doResolveException(request, response, handler, ex); <span class="comment">//留给子类的模板方法</span></span><br><span class="line">         <span class="keyword">if</span> (result != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// Print debug message when warn logger is not enabled.</span></span><br><span class="line">            <span class="keyword">if</span> (logger.isDebugEnabled() &amp;&amp; (<span class="keyword">this</span>.warnLogger == <span class="keyword">null</span> || !<span class="keyword">this</span>.warnLogger.isWarnEnabled())) &#123;</span><br><span class="line">               logger.debug(<span class="string">&quot;Resolved [&quot;</span> + ex + <span class="string">&quot;]&quot;</span> + (result.isEmpty() ? <span class="string">&quot;&quot;</span> : <span class="string">&quot; to &quot;</span> + result));</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// Explicitly configured warn logger in logException method.</span></span><br><span class="line">            logException(ex, request);</span><br><span class="line">         &#125;</span><br><span class="line">         <span class="keyword">return</span> result;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> &#123;</span><br><span class="line">         <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<h4 id="AbstractHandlerMethodExceptionResolver-doResolveException"><a href="#AbstractHandlerMethodExceptionResolver-doResolveException" class="headerlink" title="AbstractHandlerMethodExceptionResolver#doResolveException()"></a>AbstractHandlerMethodExceptionResolver#doResolveException()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> ModelAndView <span class="title">doResolveException</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">      HttpServletRequest request, HttpServletResponse response, <span class="meta">@Nullable</span> Object handler, Exception ex)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   HandlerMethod handlerMethod = (handler <span class="keyword">instanceof</span> HandlerMethod ? (HandlerMethod) handler : <span class="keyword">null</span>);</span><br><span class="line">   <span class="keyword">return</span> doResolveHandlerMethodException(request, response, handlerMethod, ex);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>下面开始进入实现类，因为index=0的是ExceptionHandlerExceptionResolver，就会先进入这个异常解析器</p>
<h4 id="ExceptionHandlerExceptionResolver-doResolveHandlerMethodException-寻找-ExceptionHandler注解标注的方法"><a href="#ExceptionHandlerExceptionResolver-doResolveHandlerMethodException-寻找-ExceptionHandler注解标注的方法" class="headerlink" title="ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()寻找@ExceptionHandler注解标注的方法"></a>ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()寻找@ExceptionHandler注解标注的方法</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">doResolveHandlerMethodException</span><span class="params">(HttpServletRequest request,</span></span></span><br><span class="line"><span class="function"><span class="params">      HttpServletResponse response, <span class="meta">@Nullable</span> HandlerMethod handlerMethod, Exception exception)</span> </span>&#123;</span><br><span class="line">   <span class="comment">//为当前异常找一个处理方法？？？  @ExceptionHandler注解标注的方法</span></span><br><span class="line">   ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);</span><br><span class="line">   <span class="keyword">if</span> (exceptionHandlerMethod == <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//异常解析器里面 还是利用了以前的 argumentResolvers和 returnValueHandlers扩展了异常解析的功能</span></span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.argumentResolvers != <span class="keyword">null</span>) &#123;</span><br><span class="line">      exceptionHandlerMethod.setHandlerMethodArgumentResolvers(<span class="keyword">this</span>.argumentResolvers);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.returnValueHandlers != <span class="keyword">null</span>) &#123;</span><br><span class="line">      exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(<span class="keyword">this</span>.returnValueHandlers);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   ServletWebRequest webRequest = <span class="keyword">new</span> ServletWebRequest(request, response);</span><br><span class="line">   ModelAndViewContainer mavContainer = <span class="keyword">new</span> ModelAndViewContainer();</span><br><span class="line"></span><br><span class="line">   ArrayList&lt;Throwable&gt; exceptions = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">         logger.debug(<span class="string">&quot;Using @ExceptionHandler &quot;</span> + exceptionHandlerMethod);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// Expose causes as provided arguments as well</span></span><br><span class="line">      Throwable exToExpose = exception;</span><br><span class="line">      <span class="keyword">while</span> (exToExpose != <span class="keyword">null</span>) &#123;</span><br><span class="line">         exceptions.add(exToExpose);</span><br><span class="line">         Throwable cause = exToExpose.getCause();</span><br><span class="line">         exToExpose = (cause != exToExpose ? cause : <span class="keyword">null</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      Object[] arguments = <span class="keyword">new</span> Object[exceptions.size() + <span class="number">1</span>];</span><br><span class="line">      exceptions.toArray(arguments);  <span class="comment">// efficient arraycopy call in ArrayList</span></span><br><span class="line">      arguments[arguments.length - <span class="number">1</span>] = handlerMethod;</span><br><span class="line">      exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> (Throwable invocationEx) &#123;</span><br><span class="line">      <span class="comment">// Any other than the original exception (or a cause) is unintended here,</span></span><br><span class="line">      <span class="comment">// probably an accident (e.g. failed assertion or the like).</span></span><br><span class="line">      <span class="keyword">if</span> (!exceptions.contains(invocationEx) &amp;&amp; logger.isWarnEnabled()) &#123;</span><br><span class="line">         logger.warn(<span class="string">&quot;Failure in @ExceptionHandler &quot;</span> + exceptionHandlerMethod, invocationEx);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// Continue with default processing of the original exception...</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (mavContainer.isRequestHandled()) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> ModelAndView();</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> &#123;</span><br><span class="line">      ModelMap model = mavContainer.getModel();</span><br><span class="line">      HttpStatus status = mavContainer.getStatus();</span><br><span class="line">      ModelAndView mav = <span class="keyword">new</span> ModelAndView(mavContainer.getViewName(), model, status);</span><br><span class="line">      mav.setViewName(mavContainer.getViewName());</span><br><span class="line">      <span class="keyword">if</span> (!mavContainer.isViewReference()) &#123;</span><br><span class="line">         mav.setView((View) mavContainer.getView());</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (model <span class="keyword">instanceof</span> RedirectAttributes) &#123;</span><br><span class="line">         Map&lt;String, ?&gt; flashAttributes = ((RedirectAttributes) model).getFlashAttributes();</span><br><span class="line">         RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> mav;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="ExceptionHandlerExceptionResolver-getExceptionHandlerMethod-遍历所有的-ControllerAdvice，看哪个类的方法能处理这个异常"><a href="#ExceptionHandlerExceptionResolver-getExceptionHandlerMethod-遍历所有的-ControllerAdvice，看哪个类的方法能处理这个异常" class="headerlink" title="ExceptionHandlerExceptionResolver#getExceptionHandlerMethod()遍历所有的@ControllerAdvice，看哪个类的方法能处理这个异常"></a>ExceptionHandlerExceptionResolver#getExceptionHandlerMethod()遍历所有的@ControllerAdvice，看哪个类的方法能处理这个异常</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ServletInvocableHandlerMethod <span class="title">getExceptionHandlerMethod</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">      <span class="meta">@Nullable</span> HandlerMethod handlerMethod, Exception exception)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   Class&lt;?&gt; handlerType = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (handlerMethod != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="comment">// Local exception handler methods on the controller class itself.</span></span><br><span class="line">      <span class="comment">// To be invoked through the proxy, even in case of an interface-based proxy.</span></span><br><span class="line">      handlerType = handlerMethod.getBeanType();</span><br><span class="line">      ExceptionHandlerMethodResolver resolver = <span class="keyword">this</span>.exceptionHandlerCache.get(handlerType);</span><br><span class="line">      <span class="keyword">if</span> (resolver == <span class="keyword">null</span>) &#123;</span><br><span class="line">         resolver = <span class="keyword">new</span> ExceptionHandlerMethodResolver(handlerType);</span><br><span class="line">         <span class="keyword">this</span>.exceptionHandlerCache.put(handlerType, resolver);</span><br><span class="line">      &#125;</span><br><span class="line">      Method method = resolver.resolveMethod(exception);</span><br><span class="line">      <span class="keyword">if</span> (method != <span class="keyword">null</span>) &#123;</span><br><span class="line">         <span class="keyword">return</span> <span class="keyword">new</span> ServletInvocableHandlerMethod(handlerMethod.getBean(), method);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// For advice applicability check below (involving base packages, assignable types</span></span><br><span class="line">      <span class="comment">// and annotation presence), use target class instead of interface-based proxy.</span></span><br><span class="line">      <span class="keyword">if</span> (Proxy.isProxyClass(handlerType)) &#123;</span><br><span class="line">         handlerType = AopUtils.getTargetClass(handlerMethod.getBean());</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//遍历所有的@ControllerAdvice，看哪个类的方法能处理这个异常</span></span><br><span class="line">   <span class="keyword">for</span> (Map.Entry&lt;ControllerAdviceBean, ExceptionHandlerMethodResolver&gt; entry : <span class="keyword">this</span>.exceptionHandlerAdviceCache.entrySet()) &#123;</span><br><span class="line">      ControllerAdviceBean advice = entry.getKey();</span><br><span class="line">      <span class="keyword">if</span> (advice.isApplicableToBeanType(handlerType)) &#123;</span><br><span class="line">         ExceptionHandlerMethodResolver resolver = entry.getValue();</span><br><span class="line">         Method method = resolver.resolveMethod(exception);</span><br><span class="line">         <span class="keyword">if</span> (method != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> ServletInvocableHandlerMethod(advice.resolveBean(), method);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="返回到ExceptionHandlerExceptionResolver-doResolveHandlerMethodException"><a href="#返回到ExceptionHandlerExceptionResolver-doResolveHandlerMethodException" class="headerlink" title="返回到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()"></a>返回到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()</h4><p>因为咱们没有@ControllerAdvice标注的类，所以这里会返回空</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021151804023.png"/>



<p>接着返回</p>
<h4 id="返回到DispatcherServlet-processHandlerException"><a href="#返回到DispatcherServlet-processHandlerException" class="headerlink" title="返回到DispatcherServlet#processHandlerException()"></a>返回到DispatcherServlet#processHandlerException()</h4><p>准备循环第二个异常解析器</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021151955136.png"/>



<p>接下来还是父类AbstractHandlerMethodExceptionResolver那个模板方法，前面写了，这里直接跳过</p>
<h4 id="ResponseStatusExceptionResolver-doResolveException-处理-ResponseStatus注解标注的相关异常"><a href="#ResponseStatusExceptionResolver-doResolveException-处理-ResponseStatus注解标注的相关异常" class="headerlink" title="ResponseStatusExceptionResolver#doResolveException()处理@ResponseStatus注解标注的相关异常"></a>ResponseStatusExceptionResolver#doResolveException()处理@ResponseStatus注解标注的相关异常</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">doResolveException</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">      HttpServletRequest request, HttpServletResponse response, <span class="meta">@Nullable</span> Object handler, Exception ex)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> ResponseStatusException) &#123;</span><br><span class="line">         <span class="keyword">return</span> resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">//拿到 ResponseStatus 注解</span></span><br><span class="line">      ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);</span><br><span class="line">      <span class="keyword">if</span> (status != <span class="keyword">null</span>) &#123;</span><br><span class="line">         <span class="keyword">return</span> resolveResponseStatus(status, request, response, handler, ex);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (ex.getCause() <span class="keyword">instanceof</span> Exception) &#123;</span><br><span class="line">         <span class="keyword">return</span> doResolveException(request, response, handler, (Exception) ex.getCause());</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> (Exception resolveEx) &#123;</span><br><span class="line">      <span class="keyword">if</span> (logger.isWarnEnabled()) &#123;</span><br><span class="line">         logger.warn(<span class="string">&quot;Failure while trying to resolve exception [&quot;</span> + ex.getClass().getName() + <span class="string">&quot;]&quot;</span>, resolveEx);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>因为咱们也没有标注@ResponseStatus注解，所以也是空，直接来到第三个异常解析器</p>
<h4 id="返回到DispatcherServlet-processHandlerException-1"><a href="#返回到DispatcherServlet-processHandlerException-1" class="headerlink" title="返回到DispatcherServlet#processHandlerException()"></a>返回到DispatcherServlet#processHandlerException()</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021152505056.png"/>







<h4 id="DefaultHandlerExceptionResolver-doResolveException-处理SpringMVC底层的异常"><a href="#DefaultHandlerExceptionResolver-doResolveException-处理SpringMVC底层的异常" class="headerlink" title="DefaultHandlerExceptionResolver#doResolveException()处理SpringMVC底层的异常"></a>DefaultHandlerExceptionResolver#doResolveException()处理SpringMVC底层的异常</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">doResolveException</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">      HttpServletRequest request, HttpServletResponse response, <span class="meta">@Nullable</span> Object handler, Exception ex)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">try</span> &#123;  <span class="comment">//处理SpringMVC底层的异常</span></span><br><span class="line">      <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> HttpRequestMethodNotSupportedException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleHttpRequestMethodNotSupported(</span><br><span class="line">               (HttpRequestMethodNotSupportedException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> HttpMediaTypeNotSupportedException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleHttpMediaTypeNotSupported(</span><br><span class="line">               (HttpMediaTypeNotSupportedException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> HttpMediaTypeNotAcceptableException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleHttpMediaTypeNotAcceptable(</span><br><span class="line">               (HttpMediaTypeNotAcceptableException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> MissingPathVariableException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleMissingPathVariable(</span><br><span class="line">               (MissingPathVariableException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> MissingServletRequestParameterException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleMissingServletRequestParameter(</span><br><span class="line">               (MissingServletRequestParameterException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> ServletRequestBindingException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleServletRequestBindingException(</span><br><span class="line">               (ServletRequestBindingException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> ConversionNotSupportedException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleConversionNotSupported(</span><br><span class="line">               (ConversionNotSupportedException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> TypeMismatchException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleTypeMismatch(</span><br><span class="line">               (TypeMismatchException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> HttpMessageNotReadableException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleHttpMessageNotReadable(</span><br><span class="line">               (HttpMessageNotReadableException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> HttpMessageNotWritableException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleHttpMessageNotWritable(</span><br><span class="line">               (HttpMessageNotWritableException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> MethodArgumentNotValidException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleMethodArgumentNotValidException(</span><br><span class="line">               (MethodArgumentNotValidException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> MissingServletRequestPartException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleMissingServletRequestPartException(</span><br><span class="line">               (MissingServletRequestPartException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> BindException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleBindException((BindException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> NoHandlerFoundException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleNoHandlerFoundException(</span><br><span class="line">               (NoHandlerFoundException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> AsyncRequestTimeoutException) &#123;</span><br><span class="line">         <span class="keyword">return</span> handleAsyncRequestTimeoutException(</span><br><span class="line">               (AsyncRequestTimeoutException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> (Exception handlerEx) &#123;</span><br><span class="line">      <span class="keyword">if</span> (logger.isWarnEnabled()) &#123;</span><br><span class="line">         logger.warn(<span class="string">&quot;Failure while trying to resolve exception [&quot;</span> + ex.getClass().getName() + <span class="string">&quot;]&quot;</span>, handlerEx);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="DefaultHandlerExceptionResolver-handleMissingServletRequestParameter-展示tomcat默认错误页"><a href="#DefaultHandlerExceptionResolver-handleMissingServletRequestParameter-展示tomcat默认错误页" class="headerlink" title="DefaultHandlerExceptionResolver#handleMissingServletRequestParameter()展示tomcat默认错误页"></a>DefaultHandlerExceptionResolver#handleMissingServletRequestParameter()展示tomcat默认错误页</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">handleMissingServletRequestParameter</span><span class="params">(MissingServletRequestParameterException ex,</span></span></span><br><span class="line"><span class="function"><span class="params">      HttpServletRequest request, HttpServletResponse response, <span class="meta">@Nullable</span> Object handler)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">   <span class="comment">//直接 sendError tomcat展示错误页</span></span><br><span class="line">   response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">new</span> ModelAndView();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这就是咱们刚刚报的错，缺少参数</p>
<h4 id="返回到DispatcherServlet-processHandlerException-2"><a href="#返回到DispatcherServlet-processHandlerException-2" class="headerlink" title="返回到DispatcherServlet#processHandlerException()"></a>返回到DispatcherServlet#processHandlerException()</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021153055017.png"/>

<p>看到这里返回了一个空的ModelAndView，并不是NULL</p>
<h4 id="返回到DispatcherServlet-processDispatchResult"><a href="#返回到DispatcherServlet-processDispatchResult" class="headerlink" title="返回到DispatcherServlet#processDispatchResult()"></a>返回到DispatcherServlet#processDispatchResult()</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021153346708.png">

<p>最后下面再处理下拦截器</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021153518821.png" >



<p>最终结束</p>
<h4 id="页面效果"><a href="#页面效果" class="headerlink" title="页面效果"></a>页面效果</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021153618095.png"/>





<h3 id="传参但报错的情况"><a href="#传参但报错的情况" class="headerlink" title="传参但报错的情况"></a>传参但报错的情况</h3><p>我们这样写：<a target="_blank" rel="noopener" href="http://localhost:8080/springmvc_source_test/hello?name=zhangsan&amp;i=0">http://localhost:8080/springmvc_source_test/hello?name=zhangsan&amp;i=0</a></p>
<blockquote>
<p>前面讲的不再重复</p>
</blockquote>
<h4 id="DispatcherServlet-doDispatch-1"><a href="#DispatcherServlet-doDispatch-1" class="headerlink" title="DispatcherServlet#doDispatch()"></a>DispatcherServlet#doDispatch()</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021154000555.png"/>



<h4 id="DispatcherServlet-processHandlerException"><a href="#DispatcherServlet-processHandlerException" class="headerlink" title="DispatcherServlet#processHandlerException()"></a>DispatcherServlet#processHandlerException()</h4><p>到了第三个异常解析器也依然处理不了，于是出现了一个谁都处理不了的异常</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021154346171.png">

<p>然后就抛出此异常</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021154530583.png"/>

<h4 id="返回到DispatcherServlet-processDispatchResult-直接炸了"><a href="#返回到DispatcherServlet-processDispatchResult-直接炸了" class="headerlink" title="返回到DispatcherServlet#processDispatchResult()直接炸了"></a>返回到DispatcherServlet#processDispatchResult()直接炸了</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021154728408.png"  />



<p>在这一步抛出了异常，整个方法直接炸了，后面的逻辑全都不走了</p>
<h4 id="返回到DispatcherServlet-doDispatch"><a href="#返回到DispatcherServlet-doDispatch" class="headerlink" title="返回到DispatcherServlet#doDispatch()"></a>返回到DispatcherServlet#doDispatch()</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021155153026.png"/>



<h4 id="DispatcherServlet-triggerAfterCompletion-抛异常"><a href="#DispatcherServlet-triggerAfterCompletion-抛异常" class="headerlink" title="DispatcherServlet#triggerAfterCompletion()抛异常"></a>DispatcherServlet#triggerAfterCompletion()抛异常</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">triggerAfterCompletion</span><span class="params">(HttpServletRequest request, HttpServletResponse response,</span></span></span><br><span class="line"><span class="function"><span class="params">      <span class="meta">@Nullable</span> HandlerExecutionChain mappedHandler, Exception ex)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (mappedHandler != <span class="keyword">null</span>) &#123;</span><br><span class="line">      mappedHandler.triggerAfterCompletion(request, response, ex);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">throw</span> ex;  <span class="comment">//抛出去</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行完拦截器，异常继续往上抛，一层一层往上抛，最终抛给了tomcat</p>
<h4 id="页面效果-1"><a href="#页面效果-1" class="headerlink" title="页面效果"></a>页面效果</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021155354597.png">

<p>这就是tomcat的默认错误页+堆栈页</p>
<h2 id="自定义异常处理"><a href="#自定义异常处理" class="headerlink" title="自定义异常处理"></a>自定义异常处理</h2><h3 id="测试类-1"><a href="#测试类-1" class="headerlink" title="测试类"></a>测试类</h3><h4 id="InvalidUserException"><a href="#InvalidUserException" class="headerlink" title="InvalidUserException"></a>InvalidUserException</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@ResponseStatus(value = HttpStatus.CONFLICT, reason = &quot;非法用户&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">InvalidUserException</span> <span class="keyword">extends</span> <span class="title">RuntimeException</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = -<span class="number">7034897190745766222L</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h4 id="HelloController-1"><a href="#HelloController-1" class="headerlink" title="HelloController"></a>HelloController</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.imlql.web.controller;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="title">HelloController</span><span class="params">()</span></span>&#123;</span><br><span class="line">      System.out.println(<span class="string">&quot;HelloController.....&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> String <span class="title">sayHello</span><span class="params">(<span class="meta">@RequestParam(name = &quot;name&quot;,required = true)</span> String name &#123;</span></span></span><br><span class="line"><span class="function"><span class="params">		<span class="keyword">if</span> (<span class="string">&quot;abc&quot;</span>.equals(name)</span>) </span>&#123;</span><br><span class="line">			<span class="comment">//非法的用户信息</span></span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> InvalidUserException();</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> <span class="string">&quot;index.jsp&quot;</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="ResponseStatusExceptionResolver处理"><a href="#ResponseStatusExceptionResolver处理" class="headerlink" title="ResponseStatusExceptionResolver处理"></a>ResponseStatusExceptionResolver处理</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">doResolveException</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">      HttpServletRequest request, HttpServletResponse response, <span class="meta">@Nullable</span> Object handler, Exception ex)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (ex <span class="keyword">instanceof</span> ResponseStatusException) &#123;</span><br><span class="line">         <span class="keyword">return</span> resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">//拿到 ResponseStatus 注解</span></span><br><span class="line">      ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);</span><br><span class="line">      <span class="keyword">if</span> (status != <span class="keyword">null</span>) &#123;</span><br><span class="line">         <span class="keyword">return</span> resolveResponseStatus(status, request, response, handler, ex);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (ex.getCause() <span class="keyword">instanceof</span> Exception) &#123;</span><br><span class="line">         <span class="keyword">return</span> doResolveException(request, response, handler, (Exception) ex.getCause());</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> (Exception resolveEx) &#123;</span><br><span class="line">      <span class="keyword">if</span> (logger.isWarnEnabled()) &#123;</span><br><span class="line">         logger.warn(<span class="string">&quot;Failure while trying to resolve exception [&quot;</span> + ex.getClass().getName() + <span class="string">&quot;]&quot;</span>, resolveEx);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021160217163.png"/>





<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">resolveResponseStatus</span><span class="params">(ResponseStatus responseStatus, HttpServletRequest request,</span></span></span><br><span class="line"><span class="function"><span class="params">      HttpServletResponse response, <span class="meta">@Nullable</span> Object handler, Exception ex)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="comment">//获取注解指定的响应状态码和错误原因</span></span><br><span class="line">   <span class="keyword">int</span> statusCode = responseStatus.code().value();</span><br><span class="line">   String reason = responseStatus.reason();</span><br><span class="line">   <span class="keyword">return</span> applyStatusAndReason(statusCode, reason, response);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">applyStatusAndReason</span><span class="params">(<span class="keyword">int</span> statusCode, <span class="meta">@Nullable</span> String reason, HttpServletResponse response)</span></span></span><br><span class="line"><span class="function">      <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (!StringUtils.hasLength(reason)) &#123;</span><br><span class="line">      response.sendError(statusCode); <span class="comment">//返回默认错误页</span></span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> &#123;</span><br><span class="line">      String resolvedReason = (<span class="keyword">this</span>.messageSource != <span class="keyword">null</span> ?</span><br><span class="line">            <span class="keyword">this</span>.messageSource.getMessage(reason, <span class="keyword">null</span>, reason, LocaleContextHolder.getLocale()) :</span><br><span class="line">            reason);</span><br><span class="line">      response.sendError(statusCode, resolvedReason);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">new</span> ModelAndView();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>页面效果</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021160409095.png">









<h2 id="最常用的注解版异常解析器-ExceptionHandler"><a href="#最常用的注解版异常解析器-ExceptionHandler" class="headerlink" title="最常用的注解版异常解析器 @ExceptionHandler"></a>最常用的注解版异常解析器 @ExceptionHandler</h2><h3 id="测试类-2"><a href="#测试类-2" class="headerlink" title="测试类"></a>测试类</h3><h4 id="MyExceptionHandler"><a href="#MyExceptionHandler" class="headerlink" title="MyExceptionHandler"></a>MyExceptionHandler</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@ControllerAdvice</span>  <span class="comment">//专门处理所有controller异常的，它是一个复合注解，里面有@Component，所以默认加在容器中</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyExceptionHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="meta">@ResponseBody</span></span><br><span class="line">   <span class="meta">@ExceptionHandler(value = &#123;ArithmeticException.class&#125;)</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> String <span class="title">handleZeroException</span><span class="params">(Exception exception)</span></span>&#123;</span><br><span class="line">      <span class="comment">//参数位置  https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler-args</span></span><br><span class="line">      <span class="comment">//返回值   https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler-return-values</span></span><br><span class="line">      <span class="keyword">return</span> <span class="string">&quot;Error&quot;</span>;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>异常处理器能写这么多参数和返回值，也是用了参数解析器，返回值处理器</p>
<h4 id="HelloController-2"><a href="#HelloController-2" class="headerlink" title="HelloController"></a>HelloController</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.imlql.web.controller;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="title">HelloController</span><span class="params">()</span></span>&#123;</span><br><span class="line">      System.out.println(<span class="string">&quot;HelloController.....&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">	<span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> String <span class="title">sayHello</span><span class="params">(Integer i)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">int</span> x = <span class="number">10</span> / i;</span><br><span class="line">		<span class="keyword">return</span> <span class="string">&quot;index.jsp&quot;</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="DispatcherServlet-doDispatch-2"><a href="#DispatcherServlet-doDispatch-2" class="headerlink" title="DispatcherServlet#doDispatch()"></a>DispatcherServlet#doDispatch()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021162737192.png"/>

<h3 id="ExceptionHandlerExceptionResolver-doResolveHandlerMethodException-寻找-ExceptionHandler注解标注的方法-1"><a href="#ExceptionHandlerExceptionResolver-doResolveHandlerMethodException-寻找-ExceptionHandler注解标注的方法-1" class="headerlink" title="ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()寻找@ExceptionHandler注解标注的方法"></a>ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()寻找@ExceptionHandler注解标注的方法</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">doResolveHandlerMethodException</span><span class="params">(HttpServletRequest request,</span></span></span><br><span class="line"><span class="function"><span class="params">      HttpServletResponse response, <span class="meta">@Nullable</span> HandlerMethod handlerMethod, Exception exception)</span> </span>&#123;</span><br><span class="line">   <span class="comment">//为当前异常找一个处理方法？？？  @ExceptionHandler注解标注的方法</span></span><br><span class="line">   ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);</span><br><span class="line">   <span class="keyword">if</span> (exceptionHandlerMethod == <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//异常解析器里面 还是利用了以前的 argumentResolvers和 returnValueHandlers扩展了异常解析的功能</span></span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.argumentResolvers != <span class="keyword">null</span>) &#123;</span><br><span class="line">      exceptionHandlerMethod.setHandlerMethodArgumentResolvers(<span class="keyword">this</span>.argumentResolvers);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.returnValueHandlers != <span class="keyword">null</span>) &#123;</span><br><span class="line">      exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(<span class="keyword">this</span>.returnValueHandlers);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   ServletWebRequest webRequest = <span class="keyword">new</span> ServletWebRequest(request, response);</span><br><span class="line">   ModelAndViewContainer mavContainer = <span class="keyword">new</span> ModelAndViewContainer();</span><br><span class="line"></span><br><span class="line">   ArrayList&lt;Throwable&gt; exceptions = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">         logger.debug(<span class="string">&quot;Using @ExceptionHandler &quot;</span> + exceptionHandlerMethod);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// Expose causes as provided arguments as well</span></span><br><span class="line">      Throwable exToExpose = exception;</span><br><span class="line">      <span class="keyword">while</span> (exToExpose != <span class="keyword">null</span>) &#123;</span><br><span class="line">         exceptions.add(exToExpose);</span><br><span class="line">         Throwable cause = exToExpose.getCause();</span><br><span class="line">         exToExpose = (cause != exToExpose ? cause : <span class="keyword">null</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      Object[] arguments = <span class="keyword">new</span> Object[exceptions.size() + <span class="number">1</span>];</span><br><span class="line">      exceptions.toArray(arguments);  <span class="comment">// efficient arraycopy call in ArrayList</span></span><br><span class="line">      arguments[arguments.length - <span class="number">1</span>] = handlerMethod;</span><br><span class="line">      exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> (Throwable invocationEx) &#123;</span><br><span class="line">      <span class="comment">// Any other than the original exception (or a cause) is unintended here,</span></span><br><span class="line">      <span class="comment">// probably an accident (e.g. failed assertion or the like).</span></span><br><span class="line">      <span class="keyword">if</span> (!exceptions.contains(invocationEx) &amp;&amp; logger.isWarnEnabled()) &#123;</span><br><span class="line">         logger.warn(<span class="string">&quot;Failure in @ExceptionHandler &quot;</span> + exceptionHandlerMethod, invocationEx);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// Continue with default processing of the original exception...</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (mavContainer.isRequestHandled()) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> ModelAndView();</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> &#123;</span><br><span class="line">      ModelMap model = mavContainer.getModel();</span><br><span class="line">      HttpStatus status = mavContainer.getStatus();</span><br><span class="line">      ModelAndView mav = <span class="keyword">new</span> ModelAndView(mavContainer.getViewName(), model, status);</span><br><span class="line">      mav.setViewName(mavContainer.getViewName());</span><br><span class="line">      <span class="keyword">if</span> (!mavContainer.isViewReference()) &#123;</span><br><span class="line">         mav.setView((View) mavContainer.getView());</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (model <span class="keyword">instanceof</span> RedirectAttributes) &#123;</span><br><span class="line">         Map&lt;String, ?&gt; flashAttributes = ((RedirectAttributes) model).getFlashAttributes();</span><br><span class="line">         RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> mav;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="ExceptionHandlerExceptionResolver-getExceptionHandlerMethod"><a href="#ExceptionHandlerExceptionResolver-getExceptionHandlerMethod" class="headerlink" title="ExceptionHandlerExceptionResolver#getExceptionHandlerMethod()"></a>ExceptionHandlerExceptionResolver#getExceptionHandlerMethod()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021164615158.png"/>

<h3 id="ExceptionHandlerMethodResolver-getMappedMethod"><a href="#ExceptionHandlerMethodResolver-getMappedMethod" class="headerlink" title="ExceptionHandlerMethodResolver#getMappedMethod()"></a>ExceptionHandlerMethodResolver#getMappedMethod()</h3><p>最终调到这里</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> Method <span class="title">getMappedMethod</span><span class="params">(Class&lt;? extends Throwable&gt; exceptionType)</span> </span>&#123;</span><br><span class="line">   List&lt;Class&lt;? extends Throwable&gt;&gt; matches = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">   <span class="keyword">for</span> (Class&lt;? extends Throwable&gt; mappedException : <span class="keyword">this</span>.mappedMethods.keySet()) &#123;</span><br><span class="line">      <span class="keyword">if</span> (mappedException.isAssignableFrom(exceptionType)) &#123;</span><br><span class="line">         matches.add(mappedException);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (!matches.isEmpty()) &#123;</span><br><span class="line">      <span class="keyword">if</span> (matches.size() &gt; <span class="number">1</span>) &#123;</span><br><span class="line">         matches.sort(<span class="keyword">new</span> ExceptionDepthComparator(exceptionType));</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>.mappedMethods.get(matches.get(<span class="number">0</span>));</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> NO_MATCHING_EXCEPTION_HANDLER_METHOD;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021165124914.png"/>





<h3 id="返回到ExceptionHandlerExceptionResolver-doResolveHandlerMethodException-1"><a href="#返回到ExceptionHandlerExceptionResolver-doResolveHandlerMethodException-1" class="headerlink" title="返回到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()"></a>返回到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021165347422.png">





<h3 id="ServletInvocableHandlerMethod-invokeAndHandle"><a href="#ServletInvocableHandlerMethod-invokeAndHandle" class="headerlink" title="ServletInvocableHandlerMethod#invokeAndHandle()"></a>ServletInvocableHandlerMethod#invokeAndHandle()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021165520356.png"/>



<h3 id="小总结"><a href="#小总结" class="headerlink" title="小总结"></a>小总结</h3><ol>
<li>可以看到异常处理的方法和springmvc的普通Controller方法最终走到了相同的反射执行逻辑</li>
<li>这也是为什么叫@ControllerAdvice，仅仅是Controller的增强</li>
</ol>
<h3 id="ExceptionHandlerExceptionResolver里的参数解析器和返回值解析器何时赋值？"><a href="#ExceptionHandlerExceptionResolver里的参数解析器和返回值解析器何时赋值？" class="headerlink" title="ExceptionHandlerExceptionResolver里的参数解析器和返回值解析器何时赋值？"></a>ExceptionHandlerExceptionResolver里的参数解析器和返回值解析器何时赋值？</h3><h4 id="ExceptionHandlerExceptionResolver-afterPropertiesSet"><a href="#ExceptionHandlerExceptionResolver-afterPropertiesSet" class="headerlink" title="ExceptionHandlerExceptionResolver#afterPropertiesSet()"></a>ExceptionHandlerExceptionResolver#afterPropertiesSet()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span>  <span class="comment">//实现了 InitilazingBean 的组件，在容器创建完对象以后，会初始化调用 InitilazingBean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterPropertiesSet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   <span class="comment">//初始化@ExceptionHandler 增强的缓存   Do this first, it may add ResponseBodyAdvice beans</span></span><br><span class="line">   initExceptionHandlerAdviceCache();</span><br><span class="line">   <span class="comment">//准备好异常解析用的参数解析器</span></span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.argumentResolvers == <span class="keyword">null</span>) &#123;</span><br><span class="line">      List&lt;HandlerMethodArgumentResolver&gt; resolvers = getDefaultArgumentResolvers();</span><br><span class="line">      <span class="keyword">this</span>.argumentResolvers = <span class="keyword">new</span> HandlerMethodArgumentResolverComposite().addResolvers(resolvers);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.returnValueHandlers == <span class="keyword">null</span>) &#123;</span><br><span class="line">      List&lt;HandlerMethodReturnValueHandler&gt; handlers = getDefaultReturnValueHandlers();</span><br><span class="line">      <span class="keyword">this</span>.returnValueHandlers = <span class="keyword">new</span> HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>还是咱们的老朋友InitializingBean，一样的逻辑，不讲了</p>
<h4 id="ExceptionHandlerExceptionResolver-initExceptionHandlerAdviceCache"><a href="#ExceptionHandlerExceptionResolver-initExceptionHandlerAdviceCache" class="headerlink" title="ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache()"></a>ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">initExceptionHandlerAdviceCache</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (getApplicationContext() == <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//找到所有的 ControllerAdviceBean(标注了@ControllerAdvice注解的类) 、</span></span><br><span class="line">   List&lt;ControllerAdviceBean&gt; adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());</span><br><span class="line">   <span class="keyword">for</span> (ControllerAdviceBean adviceBean : adviceBeans) &#123;</span><br><span class="line">      Class&lt;?&gt; beanType = adviceBean.getBeanType();</span><br><span class="line">      <span class="keyword">if</span> (beanType == <span class="keyword">null</span>) &#123;</span><br><span class="line">         <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">&quot;Unresolvable type for ControllerAdviceBean: &quot;</span> + adviceBean);</span><br><span class="line">      &#125;</span><br><span class="line">      ExceptionHandlerMethodResolver resolver = <span class="keyword">new</span> ExceptionHandlerMethodResolver(beanType);</span><br><span class="line">      <span class="keyword">if</span> (resolver.hasExceptionMappings()) &#123;</span><br><span class="line">         <span class="keyword">this</span>.exceptionHandlerAdviceCache.put(adviceBean, resolver);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (ResponseBodyAdvice.class.isAssignableFrom(beanType)) &#123;</span><br><span class="line">         <span class="keyword">this</span>.responseBodyAdvice.add(adviceBean);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">      <span class="keyword">int</span> handlerSize = <span class="keyword">this</span>.exceptionHandlerAdviceCache.size();</span><br><span class="line">      <span class="keyword">int</span> adviceSize = <span class="keyword">this</span>.responseBodyAdvice.size();</span><br><span class="line">      <span class="keyword">if</span> (handlerSize == <span class="number">0</span> &amp;&amp; adviceSize == <span class="number">0</span>) &#123;</span><br><span class="line">         logger.debug(<span class="string">&quot;ControllerAdvice beans: none&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> &#123;</span><br><span class="line">         logger.debug(<span class="string">&quot;ControllerAdvice beans: &quot;</span> +</span><br><span class="line">               handlerSize + <span class="string">&quot; @ExceptionHandler, &quot;</span> + adviceSize + <span class="string">&quot; ResponseBodyAdvice&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="ControllerAdviceBean-findAnnotatedBeans-拿到所有组件看谁标注了-ControllerAdvice"><a href="#ControllerAdviceBean-findAnnotatedBeans-拿到所有组件看谁标注了-ControllerAdvice" class="headerlink" title="ControllerAdviceBean#findAnnotatedBeans()拿到所有组件看谁标注了 @ControllerAdvice"></a>ControllerAdviceBean#findAnnotatedBeans()拿到所有组件看谁标注了 @ControllerAdvice</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> List&lt;ControllerAdviceBean&gt; <span class="title">findAnnotatedBeans</span><span class="params">(ApplicationContext context)</span> </span>&#123;</span><br><span class="line">   ListableBeanFactory beanFactory = context;</span><br><span class="line">   <span class="keyword">if</span> (context <span class="keyword">instanceof</span> ConfigurableApplicationContext) &#123;</span><br><span class="line">      <span class="comment">// Use internal BeanFactory for potential downcast to ConfigurableBeanFactory above</span></span><br><span class="line">      beanFactory = ((ConfigurableApplicationContext) context).getBeanFactory();</span><br><span class="line">   &#125;</span><br><span class="line">   List&lt;ControllerAdviceBean&gt; adviceBeans = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">   <span class="keyword">for</span> (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Object.class)) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!ScopedProxyUtils.isScopedTarget(name)) &#123; <span class="comment">//拿到所有组件看谁标注了 @ControllerAdvice</span></span><br><span class="line">         ControllerAdvice controllerAdvice = beanFactory.findAnnotationOnBean(name, ControllerAdvice.class);</span><br><span class="line">         <span class="keyword">if</span> (controllerAdvice != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// Use the @ControllerAdvice annotation found by findAnnotationOnBean()</span></span><br><span class="line">            <span class="comment">// in order to avoid a subsequent lookup of the same annotation.</span></span><br><span class="line">            adviceBeans.add(<span class="keyword">new</span> ControllerAdviceBean(name, beanFactory, controllerAdvice));</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   OrderComparator.sort(adviceBeans);</span><br><span class="line">   <span class="keyword">return</span> adviceBeans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="ExceptionHandlerMethodResolver-ExceptionHandlerMethodResolver"><a href="#ExceptionHandlerMethodResolver-ExceptionHandlerMethodResolver" class="headerlink" title="ExceptionHandlerMethodResolver#ExceptionHandlerMethodResolver()"></a>ExceptionHandlerMethodResolver#ExceptionHandlerMethodResolver()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> MethodFilter EXCEPTION_HANDLER_METHODS = method -&gt;</span><br><span class="line">		AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ExceptionHandlerMethodResolver</span><span class="params">(Class&lt;?&gt; handlerType)</span> </span>&#123; <span class="comment">//扫描当前这个ControllerAdvice中所有标注了@ExceptionHandler的方法</span></span><br><span class="line">      <span class="keyword">for</span> (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) &#123;</span><br><span class="line">         <span class="keyword">for</span> (Class&lt;? extends Throwable&gt; exceptionType : detectExceptionMappings(method)) &#123;</span><br><span class="line">            addExceptionMapping(exceptionType, method);<span class="comment">// 每一个方法能处理什么异常类型，放入到map里</span></span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>



<h4 id="ExceptionHandlerMethodResolver-addExceptionMapping"><a href="#ExceptionHandlerMethodResolver-addExceptionMapping" class="headerlink" title="ExceptionHandlerMethodResolver#addExceptionMapping()"></a>ExceptionHandlerMethodResolver#addExceptionMapping()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">   <span class="comment">//每一个方法能处理什么异常类型，缓存到Map中。</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">addExceptionMapping</span><span class="params">(Class&lt;? extends Throwable&gt; exceptionType, Method method)</span> </span>&#123;</span><br><span class="line">      Method oldMethod = <span class="keyword">this</span>.mappedMethods.put(exceptionType, method);</span><br><span class="line">      <span class="keyword">if</span> (oldMethod != <span class="keyword">null</span> &amp;&amp; !oldMethod.equals(method)) &#123;</span><br><span class="line">         <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">&quot;Ambiguous @ExceptionHandler method mapped for [&quot;</span> +</span><br><span class="line">               exceptionType + <span class="string">&quot;]: &#123;&quot;</span> + oldMethod + <span class="string">&quot;, &quot;</span> + method + <span class="string">&quot;&#125;&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Map&lt;Class&lt;? extends Throwable&gt;, Method&gt; mappedMethods = <span class="keyword">new</span> HashMap&lt;&gt;(<span class="number">16</span>);</span><br></pre></td></tr></table></figure>







<h3 id="小扩展"><a href="#小扩展" class="headerlink" title="小扩展"></a>小扩展</h3><p>你也可以自己写一个异常解析器，比如@YouthExceptionHandler，这个注解将异常信息存档，比如存到hdfs，实时看报错日志。下面是思路：</p>
<ol>
<li>我们的@YouthExceptionHandler实现InitilazingBean接口，在PropertiesSet()时，分析所有标注了@YouthExceptionHandler注解的方法，在方法执行时进行hdfs存档</li>
</ol>
<h2 id="进阶版-EnableWebMvc-WebMvcConfigurer启动Web功能"><a href="#进阶版-EnableWebMvc-WebMvcConfigurer启动Web功能" class="headerlink" title="进阶版@EnableWebMvc+WebMvcConfigurer启动Web功能"></a>进阶版@EnableWebMvc+WebMvcConfigurer启动Web功能</h2><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><ol>
<li>在以前如果我们自定义自己的组件之后，DispatcherServlet就不再用内部提供的默认组件，导致我们失去了很多默认功能。</li>
<li>比如在前面我们没讲的视图解析器，我在自己测试自定义视图解析器的时候，我发现只有自定义的视图解析器了，springmvc提供的默认视图解析器就没了。看下图</li>
</ol>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021174122938.png"/>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_11/image-20211021174320909.png"/>

<ol start="3">
<li>我们最想要的结果就是既有我们自定义的组件,也有springmvc默认提供的组件。spring也提供了解决方法@EnableWebMvc+WebMvcConfigurer</li>
<li>同时@EnableWebMvc+WebMvcConfigurer也使得扩展自定义组件变的很方便</li>
</ol>
<h3 id="测试类-MvcExtendConfiguration"><a href="#测试类-MvcExtendConfiguration" class="headerlink" title="测试类-MvcExtendConfiguration"></a>测试类-MvcExtendConfiguration</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@EnableWebMvc</span> <span class="comment">//启用SpringMVC功能</span></span><br><span class="line"><span class="meta">@Configuration</span> <span class="comment">//</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MvcExtendConfiguration</span> <span class="keyword">implements</span> <span class="title">WebMvcConfigurer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">configureViewResolvers</span><span class="params">(ViewResolverRegistry registry)</span> </span>&#123;</span><br><span class="line">		registry.viewResolver(<span class="keyword">new</span> MeiNvViewResolver());</span><br><span class="line">		<span class="comment">//不改源码就如下操作</span></span><br><span class="line">		InternalResourceViewResolver viewResolver = <span class="keyword">new</span> InternalResourceViewResolver();</span><br><span class="line">		viewResolver.setPrefix(<span class="string">&quot;&quot;</span>);</span><br><span class="line">		viewResolver.setSuffix(<span class="string">&quot;.jsp&quot;</span>); <span class="comment">//controller的返回值就不用写jsp</span></span><br><span class="line">		registry.viewResolver(viewResolver);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>WebMvcConfigurer就是给我们定制的，@EnableWebMvc就是启用默认的，下面讲讲原理</p>
<h3 id="EnableWebMvc-WebMvcConfigurer如何导入自定义组件"><a href="#EnableWebMvc-WebMvcConfigurer如何导入自定义组件" class="headerlink" title="@EnableWebMvc+WebMvcConfigurer如何导入自定义组件"></a>@EnableWebMvc+WebMvcConfigurer如何导入自定义组件</h3><h4 id="注解-EnableWebMvc"><a href="#注解-EnableWebMvc" class="headerlink" title="注解@EnableWebMvc"></a>注解@EnableWebMvc</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Target(ElementType.TYPE)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Import(DelegatingWebMvcConfiguration.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableWebMvc &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="DelegatingWebMvcConfiguration"><a href="#DelegatingWebMvcConfiguration" class="headerlink" title="DelegatingWebMvcConfiguration"></a>DelegatingWebMvcConfiguration</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DelegatingWebMvcConfiguration</span> <span class="keyword">extends</span> <span class="title">WebMvcConfigurationSupport</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">   <span class="comment">//这里是个组合关系</span></span><br><span class="line">   <span class="keyword">private</span> <span class="keyword">final</span> WebMvcConfigurerComposite configurers = <span class="keyword">new</span> WebMvcConfigurerComposite();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   <span class="meta">@Autowired(required = false)</span>  <span class="comment">//拿到容器中所有的 WebMvcConfigurer</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setConfigurers</span><span class="params">(List&lt;WebMvcConfigurer&gt; configurers)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (!CollectionUtils.isEmpty(configurers)) &#123;</span><br><span class="line">         <span class="keyword">this</span>.configurers.addWebMvcConfigurers(configurers);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configureViewResolvers</span><span class="params">(ViewResolverRegistry registry)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">this</span>.configurers.configureViewResolvers(registry);</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">addArgumentResolvers</span><span class="params">(List&lt;HandlerMethodArgumentResolver&gt; argumentResolvers)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">this</span>.configurers.addArgumentResolvers(argumentResolvers);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">addReturnValueHandlers</span><span class="params">(List&lt;HandlerMethodReturnValueHandler&gt; returnValueHandlers)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">this</span>.configurers.addReturnValueHandlers(returnValueHandlers);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configureMessageConverters</span><span class="params">(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">this</span>.configurers.configureMessageConverters(converters);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">extendMessageConverters</span><span class="params">(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">this</span>.configurers.extendMessageConverters(converters);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//...下面还有很多跟上面三个几乎一模一样的代码</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们以ViewResolvers为例</p>
<h4 id="WebMvcConfigurerComposite"><a href="#WebMvcConfigurerComposite" class="headerlink" title="WebMvcConfigurerComposite"></a>WebMvcConfigurerComposite</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">configureViewResolvers</span><span class="params">(ViewResolverRegistry registry)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">for</span> (WebMvcConfigurer delegate : <span class="keyword">this</span>.delegates) &#123;</span><br><span class="line">         delegate.configureViewResolvers(registry); <span class="comment">//最终这里会调到我们自定义的那个</span></span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addArgumentResolvers</span><span class="params">(List&lt;HandlerMethodArgumentResolver&gt; argumentResolvers)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">for</span> (WebMvcConfigurer delegate : <span class="keyword">this</span>.delegates) &#123;</span><br><span class="line">         delegate.addArgumentResolvers(argumentResolvers);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addReturnValueHandlers</span><span class="params">(List&lt;HandlerMethodReturnValueHandler&gt; returnValueHandlers)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">for</span> (WebMvcConfigurer delegate : <span class="keyword">this</span>.delegates) &#123;</span><br><span class="line">         delegate.addReturnValueHandlers(returnValueHandlers);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//......</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ol>
<li>这里最终都会调用子类的实现</li>
<li>那么@EnableWebMvc是如何导入很多默认组件。提供默认功能的呢？核心就是下面的WebMvcConfigurationSupport</li>
</ol>
<h3 id="EnableWebMvc是如何导入很多默认组件"><a href="#EnableWebMvc是如何导入很多默认组件" class="headerlink" title="@EnableWebMvc是如何导入很多默认组件"></a>@EnableWebMvc是如何导入很多默认组件</h3><h4 id="WebMvcConfigurationSupport"><a href="#WebMvcConfigurationSupport" class="headerlink" title="WebMvcConfigurationSupport"></a>WebMvcConfigurationSupport</h4><p>WebMvcConfigurationSupport是DelegatingWebMvcConfiguration的父类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebMvcConfigurationSupport</span> <span class="keyword">implements</span> <span class="title">ApplicationContextAware</span>, <span class="title">ServletContextAware</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> BeanNameUrlHandlerMapping <span class="title">beanNameHandlerMapping</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="meta">@Qualifier(&quot;mvcConversionService&quot;)</span> FormattingConversionService conversionService,</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="meta">@Qualifier(&quot;mvcResourceUrlProvider&quot;)</span> ResourceUrlProvider resourceUrlProvider)</span> </span>&#123;</span><br><span class="line">		<span class="comment">//......</span></span><br><span class="line">	</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> RequestMappingHandlerAdapter <span class="title">requestMappingHandlerAdapter</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="meta">@Qualifier(&quot;mvcContentNegotiationManager&quot;)</span> ContentNegotiationManager contentNegotiationManager,</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="meta">@Qualifier(&quot;mvcConversionService&quot;)</span> FormattingConversionService conversionService,</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="meta">@Qualifier(&quot;mvcValidator&quot;)</span> Validator validator)</span> </span>&#123;</span><br><span class="line">     	<span class="comment">//......</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> ViewResolver <span class="title">mvcViewResolver</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="meta">@Qualifier(&quot;mvcContentNegotiationManager&quot;)</span> ContentNegotiationManager contentNegotiationManager)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//......</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">	<span class="meta">@Lazy</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> HandlerMappingIntrospector <span class="title">mvcHandlerMappingIntrospector</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> HandlerMappingIntrospector();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> LocaleResolver <span class="title">localeResolver</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> AcceptHeaderLocaleResolver();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> ThemeResolver <span class="title">themeResolver</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> FixedThemeResolver();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> FlashMapManager <span class="title">flashMapManager</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> SessionFlashMapManager();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> RequestToViewNameTranslator <span class="title">viewNameTranslator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> DefaultRequestToViewNameTranslator();</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">        </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">	<span class="meta">@SuppressWarnings(&quot;deprecation&quot;)</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> RequestMappingHandlerMapping <span class="title">requestMappingHandlerMapping</span><span class="params">(<span class="meta">@Qualifier(&quot;mvcContentNegotiationManager&quot;)</span> ContentNegotiationManager contentNegotiationManager,</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="meta">@Qualifier(&quot;mvcConversionService&quot;)</span> FormattingConversionService conversionService,</span></span></span><br><span class="line"><span class="function"><span class="params">			<span class="meta">@Qualifier(&quot;mvcResourceUrlProvider&quot;)</span> ResourceUrlProvider resourceUrlProvider)</span></span>&#123;</span><br><span class="line">         RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();</span><br><span class="line">		mapping.setOrder(<span class="number">0</span>);</span><br><span class="line">		mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));</span><br><span class="line">		mapping.setContentNegotiationManager(contentNegotiationManager);</span><br><span class="line">		mapping.setCorsConfigurations(getCorsConfigurations());</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//......</span></span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol>
<li>此类里面放了很多默认组件，包括九大组件还有很多我没列举出来的</li>
</ol>
<h4 id="WebMvcConfigurationSupport-getInterceptors"><a href="#WebMvcConfigurationSupport-getInterceptors" class="headerlink" title="WebMvcConfigurationSupport#getInterceptors()"></a>WebMvcConfigurationSupport#getInterceptors()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> Object[] getInterceptors(</span><br><span class="line">      FormattingConversionService mvcConversionService,</span><br><span class="line">      ResourceUrlProvider mvcResourceUrlProvider) &#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.interceptors == <span class="keyword">null</span>) &#123;</span><br><span class="line">      InterceptorRegistry registry = <span class="keyword">new</span> InterceptorRegistry();</span><br><span class="line">      addInterceptors(registry); <span class="comment">//子类模板先来修改 registry</span></span><br><span class="line">      registry.addInterceptor(<span class="keyword">new</span> ConversionServiceExposingInterceptor(mvcConversionService));</span><br><span class="line">      registry.addInterceptor(<span class="keyword">new</span> ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));</span><br><span class="line">      <span class="keyword">this</span>.interceptors = registry.getInterceptors();</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">this</span>.interceptors.toArray();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol start="2">
<li>导入了这么多组件，这些组件在关键时刻会调用子类重写的方法，比如上面面的addInterceptors(registry);会先调用</li>
</ol>
<p>子类<code>DelegatingWebMvcConfiguration#addInterceptors()</code></p>
<h4 id="DelegatingWebMvcConfiguration-addInterceptors"><a href="#DelegatingWebMvcConfiguration-addInterceptors" class="headerlink" title="DelegatingWebMvcConfiguration#addInterceptors()"></a>DelegatingWebMvcConfiguration#addInterceptors()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">addInterceptors</span><span class="params">(InterceptorRegistry registry)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">this</span>.configurers.addInterceptors(registry); <span class="comment">//如果我们继承了WebMvcConfigurer，并且重写了addInterceptors，这里就会加进来</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="3">
<li>也就是每一个组件的关键核心位置都留给了子类来重写</li>
</ol>
<h3 id="为什么自定义视图解析器会覆盖默认的视图解析器？"><a href="#为什么自定义视图解析器会覆盖默认的视图解析器？" class="headerlink" title="为什么自定义视图解析器会覆盖默认的视图解析器？"></a>为什么自定义视图解析器会覆盖默认的视图解析器？</h3><h4 id="WebMvcConfigurationSupport-mvcViewResolver"><a href="#WebMvcConfigurationSupport-mvcViewResolver" class="headerlink" title="WebMvcConfigurationSupport#mvcViewResolver()"></a>WebMvcConfigurationSupport#mvcViewResolver()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Bean</span>  <span class="comment">//给容器中放了视图解析器，容器中一开始就有mvcViewResolver这个bean的定义信息</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ViewResolver <span class="title">mvcViewResolver</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">      <span class="meta">@Qualifier(&quot;mvcContentNegotiationManager&quot;)</span> ContentNegotiationManager contentNegotiationManager)</span> </span>&#123;</span><br><span class="line">   ViewResolverRegistry registry =</span><br><span class="line">         <span class="keyword">new</span> ViewResolverRegistry(contentNegotiationManager, <span class="keyword">this</span>.applicationContext);</span><br><span class="line">   configureViewResolvers(registry); <span class="comment">//前面自己扩展配置视图解析器，</span></span><br><span class="line">   <span class="comment">//只有不自定义视图解析器，才会给容器放入默认视图解析器</span></span><br><span class="line">   <span class="keyword">if</span> (registry.getViewResolvers().isEmpty() || <span class="keyword">this</span>.applicationContext != <span class="keyword">null</span>) &#123;</span><br><span class="line">      String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( <span class="comment">//容器中只有一个组件的定义</span></span><br><span class="line">            <span class="keyword">this</span>.applicationContext, ViewResolver.class, <span class="keyword">true</span>, <span class="keyword">false</span>);</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">true</span> || names.length == <span class="number">1</span>) &#123; <span class="comment">//容器中有视图解析器，会把 InternalResourceViewResolver 放在容器中</span></span><br><span class="line">         registry.getViewResolvers().add(<span class="keyword">new</span> InternalResourceViewResolver()); <span class="comment">//总是把默认的加入进去</span></span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//我们如果扩展配置了，完全按照我们configureViewResolvers，如果没有配置，用默认</span></span><br><span class="line">   ViewResolverComposite composite = <span class="keyword">new</span> ViewResolverComposite();</span><br><span class="line">   composite.setOrder(registry.getOrder());</span><br><span class="line">   composite.setViewResolvers(registry.getViewResolvers());</span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.applicationContext != <span class="keyword">null</span>) &#123;</span><br><span class="line">      composite.setApplicationContext(<span class="keyword">this</span>.applicationContext);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.servletContext != <span class="keyword">null</span>) &#123;</span><br><span class="line">      composite.setServletContext(<span class="keyword">this</span>.servletContext);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> composite;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>所以如果即想要自定义视图解析器，又想要默认的，就下面这样写</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@EnableWebMvc</span> <span class="comment">//启用SpringMVC功能</span></span><br><span class="line"><span class="meta">@Configuration</span> <span class="comment">//</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MvcExtendConfiguration</span> <span class="keyword">implements</span> <span class="title">WebMvcConfigurer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">configureViewResolvers</span><span class="params">(ViewResolverRegistry registry)</span> </span>&#123;</span><br><span class="line">        registry.viewResolver(<span class="keyword">new</span> MeiNvViewResolver());</span><br><span class="line">        <span class="comment">//不改源码就如下操作</span></span><br><span class="line">        InternalResourceViewResolver viewResolver = <span class="keyword">new</span> InternalResourceViewResolver();</span><br><span class="line">        viewResolver.setPrefix(<span class="string">&quot;&quot;</span>);</span><br><span class="line">        viewResolver.setSuffix(<span class="string">&quot;.jsp&quot;</span>); <span class="comment">//controller的返回值就不用写jsp</span></span><br><span class="line">        registry.viewResolver(viewResolver);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其它组件可能也会有默认组件失效的情况，可以触类旁通看下源码</p>
<ol>
<li>@EnableWebMvc导入的类会加入SpringMVC的很多核心默认组件，拥有默认功能</li>
<li>这些默认组件在扩展的时候都是留给接口 WebMvcConfigurer（访问者模式，拿到真正的内容，比如上面的registry进行修改）<br>4、MeiNvViewResolver+InternalResourceViewResolver<br>5、@EnableWebMvc只是开启了SpringMVC最基本的功能，即使是以前自己也要配置默认视图解析器</li>
</ol>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">youthlql</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://imlql.cn/post/4dfb096b.html">https://imlql.cn/post/4dfb096b.html</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://imlql.cn" target="_blank">风祈的时光录</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Spring%E6%BA%90%E7%A0%81/">Spring源码</a></div><div class="post_share"></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏</div><div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" alt="微信"/></a><div class="post-qr-code-desc">微信</div></li><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" alt="支付宝"/></a><div class="post-qr-code-desc">支付宝</div></li></ul></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/post/e3da4d66.html"><img class="prev-cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Spring源码系列-第12章-SpringBoot源码-自动配置原理和内嵌Tomcat启动原理</div></div></a></div><div class="next-post pull-right"><a href="/post/453f802e.html"><img class="next-cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">Spring源码系列-第10章-SpringMVC中的HandlerAdapter源码解析</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/post/41fb8d9e.html" title="Spring源码系列-第2章-后置工厂处理器和Bean生命周期"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-12-21</div><div class="title">Spring源码系列-第2章-后置工厂处理器和Bean生命周期</div></div></a></div><div><a href="/post/49f419ae.html" title="Spring源码系列-第4章-Bean初始化流程"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-01-27</div><div class="title">Spring源码系列-第4章-Bean初始化流程</div></div></a></div><div><a href="/post/fb3552e0.html" title="Spring源码系列-第7章-AOP的执行流程原理和监听器原理"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-04-17</div><div class="title">Spring源码系列-第7章-AOP的执行流程原理和监听器原理</div></div></a></div><div><a href="/post/c8dd1418.html" title="Spring源码系列-第8章-SpringMVC子容器和Spring父容器的启动原理"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-06-20</div><div class="title">Spring源码系列-第8章-SpringMVC子容器和Spring父容器的启动原理</div></div></a></div><div><a href="/post/6f2cef28.html" title="Spring源码系列-第9章-SpringMVC请求处理源码和HandlerMapping原理"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-06-21</div><div class="title">Spring源码系列-第9章-SpringMVC请求处理源码和HandlerMapping原理</div></div></a></div><div><a href="/post/e3da4d66.html" title="Spring源码系列-第12章-SpringBoot源码-自动配置原理和内嵌Tomcat启动原理"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-01-06</div><div class="title">Spring源码系列-第12章-SpringBoot源码-自动配置原理和内嵌Tomcat启动原理</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E7%AC%AC11%E7%AB%A0-SpringMVC%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E6%BA%90%E7%A0%81%E5%92%8C-EnableWebMvc%E5%8E%9F%E7%90%86"><span class="toc-text">第11章-SpringMVC异常处理源码和@EnableWebMvc原理</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%A7%86%E5%9B%BE%E8%A7%A3%E6%9E%90%E5%99%A8%E4%B8%8D%E9%87%8D%E8%A6%81%E4%BA%86%EF%BC%8C%E4%B8%8D%E7%BB%86%E8%BF%B0%E4%BA%86"><span class="toc-text">视图解析器不重要了，不细述了</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B"><span class="toc-text">异常处理流程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95%E7%B1%BB"><span class="toc-text">测试类</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#HelloController"><span class="toc-text">HelloController</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E5%8F%82%E6%95%B0%E9%83%BD%E4%B8%8D%E4%BC%A0"><span class="toc-text">什么参数都不传</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#DispatcherServlet-doDispatch"><span class="toc-text">DispatcherServlet#doDispatch()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DispatcherServlet-processDispatchResult"><span class="toc-text">DispatcherServlet#processDispatchResult()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DispatcherServlet-processHandlerException-%E5%87%86%E5%A4%87%E5%A4%84%E7%90%86%E5%BC%82%E5%B8%B8"><span class="toc-text">DispatcherServlet#processHandlerException()准备处理异常</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%B8%89%E4%B8%AA%E5%BC%82%E5%B8%B8%E8%A7%A3%E6%9E%90%E5%99%A8%E6%A6%82%E8%BF%B0"><span class="toc-text">三个异常解析器概述</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AbstractHandlerExceptionResolver-resolveException-%E8%A7%A3%E6%9E%90%E5%BC%82%E5%B8%B8"><span class="toc-text">AbstractHandlerExceptionResolver#resolveException()解析异常</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AbstractHandlerMethodExceptionResolver-doResolveException"><span class="toc-text">AbstractHandlerMethodExceptionResolver#doResolveException()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ExceptionHandlerExceptionResolver-doResolveHandlerMethodException-%E5%AF%BB%E6%89%BE-ExceptionHandler%E6%B3%A8%E8%A7%A3%E6%A0%87%E6%B3%A8%E7%9A%84%E6%96%B9%E6%B3%95"><span class="toc-text">ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()寻找@ExceptionHandler注解标注的方法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ExceptionHandlerExceptionResolver-getExceptionHandlerMethod-%E9%81%8D%E5%8E%86%E6%89%80%E6%9C%89%E7%9A%84-ControllerAdvice%EF%BC%8C%E7%9C%8B%E5%93%AA%E4%B8%AA%E7%B1%BB%E7%9A%84%E6%96%B9%E6%B3%95%E8%83%BD%E5%A4%84%E7%90%86%E8%BF%99%E4%B8%AA%E5%BC%82%E5%B8%B8"><span class="toc-text">ExceptionHandlerExceptionResolver#getExceptionHandlerMethod()遍历所有的@ControllerAdvice，看哪个类的方法能处理这个异常</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0ExceptionHandlerExceptionResolver-doResolveHandlerMethodException"><span class="toc-text">返回到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0DispatcherServlet-processHandlerException"><span class="toc-text">返回到DispatcherServlet#processHandlerException()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ResponseStatusExceptionResolver-doResolveException-%E5%A4%84%E7%90%86-ResponseStatus%E6%B3%A8%E8%A7%A3%E6%A0%87%E6%B3%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E5%BC%82%E5%B8%B8"><span class="toc-text">ResponseStatusExceptionResolver#doResolveException()处理@ResponseStatus注解标注的相关异常</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0DispatcherServlet-processHandlerException-1"><span class="toc-text">返回到DispatcherServlet#processHandlerException()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DefaultHandlerExceptionResolver-doResolveException-%E5%A4%84%E7%90%86SpringMVC%E5%BA%95%E5%B1%82%E7%9A%84%E5%BC%82%E5%B8%B8"><span class="toc-text">DefaultHandlerExceptionResolver#doResolveException()处理SpringMVC底层的异常</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DefaultHandlerExceptionResolver-handleMissingServletRequestParameter-%E5%B1%95%E7%A4%BAtomcat%E9%BB%98%E8%AE%A4%E9%94%99%E8%AF%AF%E9%A1%B5"><span class="toc-text">DefaultHandlerExceptionResolver#handleMissingServletRequestParameter()展示tomcat默认错误页</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0DispatcherServlet-processHandlerException-2"><span class="toc-text">返回到DispatcherServlet#processHandlerException()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0DispatcherServlet-processDispatchResult"><span class="toc-text">返回到DispatcherServlet#processDispatchResult()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%A1%B5%E9%9D%A2%E6%95%88%E6%9E%9C"><span class="toc-text">页面效果</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BC%A0%E5%8F%82%E4%BD%86%E6%8A%A5%E9%94%99%E7%9A%84%E6%83%85%E5%86%B5"><span class="toc-text">传参但报错的情况</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#DispatcherServlet-doDispatch-1"><span class="toc-text">DispatcherServlet#doDispatch()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DispatcherServlet-processHandlerException"><span class="toc-text">DispatcherServlet#processHandlerException()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0DispatcherServlet-processDispatchResult-%E7%9B%B4%E6%8E%A5%E7%82%B8%E4%BA%86"><span class="toc-text">返回到DispatcherServlet#processDispatchResult()直接炸了</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0DispatcherServlet-doDispatch"><span class="toc-text">返回到DispatcherServlet#doDispatch()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DispatcherServlet-triggerAfterCompletion-%E6%8A%9B%E5%BC%82%E5%B8%B8"><span class="toc-text">DispatcherServlet#triggerAfterCompletion()抛异常</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%A1%B5%E9%9D%A2%E6%95%88%E6%9E%9C-1"><span class="toc-text">页面效果</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86"><span class="toc-text">自定义异常处理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95%E7%B1%BB-1"><span class="toc-text">测试类</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#InvalidUserException"><span class="toc-text">InvalidUserException</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#HelloController-1"><span class="toc-text">HelloController</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ResponseStatusExceptionResolver%E5%A4%84%E7%90%86"><span class="toc-text">ResponseStatusExceptionResolver处理</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9C%80%E5%B8%B8%E7%94%A8%E7%9A%84%E6%B3%A8%E8%A7%A3%E7%89%88%E5%BC%82%E5%B8%B8%E8%A7%A3%E6%9E%90%E5%99%A8-ExceptionHandler"><span class="toc-text">最常用的注解版异常解析器 @ExceptionHandler</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95%E7%B1%BB-2"><span class="toc-text">测试类</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#MyExceptionHandler"><span class="toc-text">MyExceptionHandler</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#HelloController-2"><span class="toc-text">HelloController</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-doDispatch-2"><span class="toc-text">DispatcherServlet#doDispatch()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ExceptionHandlerExceptionResolver-doResolveHandlerMethodException-%E5%AF%BB%E6%89%BE-ExceptionHandler%E6%B3%A8%E8%A7%A3%E6%A0%87%E6%B3%A8%E7%9A%84%E6%96%B9%E6%B3%95-1"><span class="toc-text">ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()寻找@ExceptionHandler注解标注的方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ExceptionHandlerExceptionResolver-getExceptionHandlerMethod"><span class="toc-text">ExceptionHandlerExceptionResolver#getExceptionHandlerMethod()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ExceptionHandlerMethodResolver-getMappedMethod"><span class="toc-text">ExceptionHandlerMethodResolver#getMappedMethod()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0ExceptionHandlerExceptionResolver-doResolveHandlerMethodException-1"><span class="toc-text">返回到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ServletInvocableHandlerMethod-invokeAndHandle"><span class="toc-text">ServletInvocableHandlerMethod#invokeAndHandle()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%8F%E6%80%BB%E7%BB%93"><span class="toc-text">小总结</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ExceptionHandlerExceptionResolver%E9%87%8C%E7%9A%84%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8%E5%92%8C%E8%BF%94%E5%9B%9E%E5%80%BC%E8%A7%A3%E6%9E%90%E5%99%A8%E4%BD%95%E6%97%B6%E8%B5%8B%E5%80%BC%EF%BC%9F"><span class="toc-text">ExceptionHandlerExceptionResolver里的参数解析器和返回值解析器何时赋值？</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#ExceptionHandlerExceptionResolver-afterPropertiesSet"><span class="toc-text">ExceptionHandlerExceptionResolver#afterPropertiesSet()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ExceptionHandlerExceptionResolver-initExceptionHandlerAdviceCache"><span class="toc-text">ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ControllerAdviceBean-findAnnotatedBeans-%E6%8B%BF%E5%88%B0%E6%89%80%E6%9C%89%E7%BB%84%E4%BB%B6%E7%9C%8B%E8%B0%81%E6%A0%87%E6%B3%A8%E4%BA%86-ControllerAdvice"><span class="toc-text">ControllerAdviceBean#findAnnotatedBeans()拿到所有组件看谁标注了 @ControllerAdvice</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ExceptionHandlerMethodResolver-ExceptionHandlerMethodResolver"><span class="toc-text">ExceptionHandlerMethodResolver#ExceptionHandlerMethodResolver()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ExceptionHandlerMethodResolver-addExceptionMapping"><span class="toc-text">ExceptionHandlerMethodResolver#addExceptionMapping()</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%8F%E6%89%A9%E5%B1%95"><span class="toc-text">小扩展</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%BF%9B%E9%98%B6%E7%89%88-EnableWebMvc-WebMvcConfigurer%E5%90%AF%E5%8A%A8Web%E5%8A%9F%E8%83%BD"><span class="toc-text">进阶版@EnableWebMvc+WebMvcConfigurer启动Web功能</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0"><span class="toc-text">概述</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95%E7%B1%BB-MvcExtendConfiguration"><span class="toc-text">测试类-MvcExtendConfiguration</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#EnableWebMvc-WebMvcConfigurer%E5%A6%82%E4%BD%95%E5%AF%BC%E5%85%A5%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6"><span class="toc-text">@EnableWebMvc+WebMvcConfigurer如何导入自定义组件</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B3%A8%E8%A7%A3-EnableWebMvc"><span class="toc-text">注解@EnableWebMvc</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DelegatingWebMvcConfiguration"><span class="toc-text">DelegatingWebMvcConfiguration</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#WebMvcConfigurerComposite"><span class="toc-text">WebMvcConfigurerComposite</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#EnableWebMvc%E6%98%AF%E5%A6%82%E4%BD%95%E5%AF%BC%E5%85%A5%E5%BE%88%E5%A4%9A%E9%BB%98%E8%AE%A4%E7%BB%84%E4%BB%B6"><span class="toc-text">@EnableWebMvc是如何导入很多默认组件</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#WebMvcConfigurationSupport"><span class="toc-text">WebMvcConfigurationSupport</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#WebMvcConfigurationSupport-getInterceptors"><span class="toc-text">WebMvcConfigurationSupport#getInterceptors()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DelegatingWebMvcConfiguration-addInterceptors"><span class="toc-text">DelegatingWebMvcConfiguration#addInterceptors()</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%87%AA%E5%AE%9A%E4%B9%89%E8%A7%86%E5%9B%BE%E8%A7%A3%E6%9E%90%E5%99%A8%E4%BC%9A%E8%A6%86%E7%9B%96%E9%BB%98%E8%AE%A4%E7%9A%84%E8%A7%86%E5%9B%BE%E8%A7%A3%E6%9E%90%E5%99%A8%EF%BC%9F"><span class="toc-text">为什么自定义视图解析器会覆盖默认的视图解析器？</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#WebMvcConfigurationSupport-mvcViewResolver"><span class="toc-text">WebMvcConfigurationSupport#mvcViewResolver()</span></a></li></ol></li></ol></li></ol></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By youthlql</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text"><a href="https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral" target="_blank" rel="noopener" class="one-pan-link-mark"><img style="position:relative;top:-3px; " src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/upyun/logo.png" align="absmiddle" width="60px" height="30px"></a><a target="_blank" rel="noopener" href="https://beian.miit.gov.cn"><img class="icp-icon" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/icp.png"><span>鄂ICP备19028890号</span></a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">简</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">本地搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.umd.js"></script><script src="https://npm.elemecdn.com/instant.page/instantpage.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script defer src="https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js"></script></div></body></html>